/*
 * JAKE ELLOWITZ
 *
 * A simulation of a 2D ising model. 
 *
 * IMPORTANT: state of lattice array must always be 0 (down) or 1 (up)
 *
 * Inputs: output base name, h, t, method
 *
 * TODO: Add meethod #defines in header for cross-program consistency.
 */

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime>

#include <GL/gl.h>
#include <GL/glut.h>
#define SLEEP_TICKS 10

#include "utils.h"

#define L 128
#define DIM 2
// GL window stuff
#define WINDOW_L 1*L

// Method 0 is checkerboard, 1 is random
#define METHOD 1

#define DOWN 0
#define UP 1

// Spin-spin interaction
#define J 1.0
// External magnetic field
//#define H 0.225
#define H -0.00
#define T 2.269*4/9

#define DATA_BUFF 200
#define SETTLETIME 1000
#define MAXRUNTIME 1000000

#include "data.h"
#include "ising.h"

using namespace std;

// The class is initialized below based on input parameters.
static ising * I;
static data * D;

// Function declarations
void draw_square (double, double, double, uint8);
void draw_point (double, double, uint8);
void display_update (uint32, uint32, uint8 *);
void timercall (int);
void swap_mag_field (int, int, int, int);
void glut_init (int, char **);

// OPENGL
//

void draw_square (double x, double y, double l, uint8 spin)
{
	if (spin == DOWN) glColor3f (1.0, 1.0, 0.0);
	else if (spin == UP) glColor3f (0.0, 0.0, 1.0);
	else printf ("unrecongized spin\n");
	glBegin (GL_QUADS);
	glVertex2f (x  ,  y);
	glVertex2f (x+l,  y);
	glVertex2f (x+l,y+l);
	glVertex2f (x  ,y+l);
	glEnd ();
}

void draw_point (double x, double y, uint8 spin)
{
	if (spin == DOWN) glColor3f (1.0, 1.0, 0.0);
	else if (spin == UP) glColor3f (0.0, 0.0, 1.0);
	else printf ("unrecongized spin\n");
	glBegin (GL_POINTS);
	glVertex2f (x,y);
	glEnd();
}

void display_update (uint32 len, uint32 n_sites, uint8 * lat)
{
	static int i;
	static double x, y, l;

	l = (double) WINDOW_L/len/len;

	for (i=0; i<(int)n_sites; ++i)
	{
		x = (double)(i%len - (double)len/2)*2/len;
		y = (double)((i/len)%I->len - (double)len/2)*2/len;
		draw_square (x, y, l, lat[i]);
		//draw_point (x, y, I->lat[i]);
	}
}

void timercall (int value)
{
	glutTimerFunc (SLEEP_TICKS, timercall, 0);
	I->step (METHOD);
	display_update (I->len, I->n_sites, I->lat);
	glutPostRedisplay ();
}

// Glut makes us do this round about...
void swap_mag_field (int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
		I->swap_mag_field ();
}

void glut_init (int argc, char ** argv)
{
	glutInit (&argc, argv);
	glutInitDisplayMode (GLUT_SINGLE);
	glutInitWindowSize (WINDOW_L, WINDOW_L);
	glutCreateWindow ("Ising Simulation");
	glutTimerFunc (SLEEP_TICKS, timercall, 0);
	glutMouseFunc (swap_mag_field);
}

// END OPENGL
//

// FILE OUTPUT
//

bool file_exists (char * name)
{
	if (FILE * test = fopen (name, "r"))
	{
		fclose (test);
		return true;
	}
	else
		return false;
}

// END FILE OUTPUT
//

int main (int argc, char ** argv)
{
	srand (time (NULL));

	int i;
	double h = H;
	double tau = T;
	char name [100];
	sprintf (name, "./data/default");
	int method = METHOD;

	if (argc >1)
		sprintf (name, "./data/%s", argv[1]);
	if (argc > 2)
		h = atof (argv[2]);
	if (argc > 3)
		tau = atof (argv[3]);
	if (argc > 4)
		method = atoi (argv[4]);

	I = new ising (L, DIM, h, J, tau);
	D = new data (DATA_BUFF, name);

//	glut_init (argc, argv);
//	glutMainLoop ();
	
	// Let the system settle first, then swap the field and collect data.
	for (i=0; i<SETTLETIME; ++i)
		I->step (method);

	I->swap_mag_field ();

	for (i=0; i<MAXRUNTIME; ++i)
	{
		// data step retunrs 1 whn field and m align --  a sign that nucleation
		// has ocurred.
		if (D->data_step (I->lat, I->n_sites, I->t, I->h))
			break;
		I->step (method);
	}
	return 0;
}
